#include <gtest/gtest.h>
#include <gmock/gmock.h>

#define private public
#define protected public
#include <material.h>
#undef private
#undef protected

using namespace std;
using ::testing::ContainerEq;


class UniIdealTest: public::testing::Test
{
    protected:
        // Declares the UniIdeals
        UniIdeal mat1_ = UniIdeal(1, 0, 0);
        UniIdeal mat2_ = UniIdeal(2, 1000, 20);
        UniIdeal mat3_ = UniIdeal(3, 0, 0);
        UniIdeal mat4_ = UniIdeal(4, 0, 0);

        // other data
        double stress = 0, strain = 0, ps = 0, alpha = 0, q = 0;

        // You can remove any or all of the following functions if their bodies
        // would be empty.
        UniIdealTest()
        {
            // You can do set-up work for each test here.
        };

        ~UniIdealTest()
        {
            // You can do clean-up work that doesn't throw exceptions here.
        };

        // If the constructor and destructor are not enough for setting up
        // and cleaning up each test, you can define the following methods:
        void SetUp() override
        {
            // Code here will be called immediately after the constructor
            // (right before each test).
            cout << "Set Up UniIdeal Test" << endl;
        };

        void TearDown() override
        {
            // Code here will be called immediately after each test (right
            // before the destructor).
        };

        // Class members declared here can be used by all tests in the test
        // suite for Foo.
};


// Tests that the UniIdeal::UniIdeal() method does Abc.
TEST_F(UniIdealTest, IsEmptyInitiate)
{
    cout << "\n---------- UniIdealTest IsEmptyInitiate ----------" << endl;
    cout << "check id... " << endl;
    EXPECT_EQ(mat1_.id(), 1);
    EXPECT_EQ(mat2_.id(), 2);
    EXPECT_EQ(mat3_.id(), 3);
    EXPECT_EQ(mat4_.id(), 4);

    cout << "check type... " << endl;
    char type[] = "UniIdeal";
    EXPECT_STREQ(type, mat1_.type_.c_str());

    cout << "check E... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.E_);

    cout << "check G... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.G_);

    cout << "check nu... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.nu_);

    cout << "check density... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.density_);

    cout << "check ce... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.ce_);

    cout << "check damp_ratio... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.damp_ratio_);

    cout << "check damp_coef... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.damp_coef_);

    cout << "check mu... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.mu_);

    cout << "check yield_stress... " << endl;
    EXPECT_DOUBLE_EQ(0.0, mat1_.yield_stress_);

    cout << "check limited_tensile_strain... " << endl;
    EXPECT_DOUBLE_EQ(9.99e99, mat1_.limited_tensile_strain_);

    cout << "check limited_compress_strain... " << endl;
    EXPECT_DOUBLE_EQ(-9.99e99, mat1_.limited_compress_strain_);

    cout << "check m... " << endl;
    EXPECT_DOUBLE_EQ(0, mat1_.m_);

    cout << "check Et... " << endl;
    EXPECT_DOUBLE_EQ(0, mat1_.Et_);
};


// Tests that the UniIdeal::isFracture() method.
TEST_F(UniIdealTest, isFracture)
{
    cout << "\n---------- UniIdealTest isFracture ----------" << endl;
    double val1 = 0.03, val2 = -0.04;
    mat1_.setLimitedStrain(val1, val2);
    EXPECT_FALSE(mat1_.isFractured(0.0));
    EXPECT_FALSE(mat1_.isFractured(0.01));
    EXPECT_FALSE(mat1_.isFractured(-0.01));

    EXPECT_TRUE(mat1_.isFractured(0.03));
    EXPECT_TRUE(mat1_.isFractured(-0.04));

    EXPECT_TRUE(mat1_.isFractured(0.05));
    EXPECT_TRUE(mat1_.isFractured(-0.04));
};


// Tests that the UniIdeal::setE() method.
TEST_F(UniIdealTest, setE)
{
    cout << "\n---------- UniIdealTest setE ----------" << endl;
    double val = 2000;
    mat1_.setE(val);
    EXPECT_DOUBLE_EQ(val, mat1_.E_);

    // input an negative value
    val = -100;
    mat1_.setE(val);
    EXPECT_DOUBLE_EQ(val, mat1_.E_);
};


// Tests that the UniIdeal::setG() method does Abc.
TEST_F(UniIdealTest, setG)
{
    cout << "\n---------- UniIdealTest setG ----------" << endl;
    double val = 2000;
    mat1_.setG(val);
    EXPECT_DOUBLE_EQ(val, mat1_.G_);

    // input an negative value
    val = -100;
    mat1_.setG(val);
    EXPECT_DOUBLE_EQ(val, mat1_.G_);
};


// Tests that the UniIdeal::setNu() method does Abc.
TEST_F(UniIdealTest, setNu)
{
    cout << "\n---------- UniIdealTest setNu ----------" << endl;
    double val = 2000;
    mat1_.setNu(val);
    EXPECT_DOUBLE_EQ(val, mat1_.nu_);
    double G = mat1_.E_ / 2.0 / (1 + val);
    EXPECT_DOUBLE_EQ(G, mat1_.G_);

    // input an negative value
    val = -100;
    mat1_.setNu(val);
    EXPECT_DOUBLE_EQ(val, mat1_.nu_);
    G = mat1_.E_ / 2.0 / (1 + val);
    EXPECT_DOUBLE_EQ(G, mat1_.G_);
};


// Tests that the UniIdeal::setDensity() method does Abc.
TEST_F(UniIdealTest, setDensity)
{
    cout << "\n---------- UniIdealTest setDensity ----------" << endl;
    double val = 2000;
    mat1_.setDensity(val);
    EXPECT_DOUBLE_EQ(val, mat1_.density_);

    // input an negative value
    val = -100;
    mat1_.setDensity(val);
    EXPECT_DOUBLE_EQ(val, mat1_.density_);
};


// Tests that the UniIdeal::calcCe() method does Abc.
TEST_F(UniIdealTest, calcCe)
{
    cout << "\n---------- UniIdealTest calcCe ----------" << endl;
    // EXPECT_DOUBLE_EQ(0.0/0.0, mat1_.calcCe());
    double E = 2000, density = 20;
    mat1_.setE(E);
    mat1_.setDensity(density);
    EXPECT_DOUBLE_EQ(sqrt(E/density), mat1_.calcCe());
};


// Tests that the UniIdeal::setDampRatio() method does Abc.
TEST_F(UniIdealTest, setDampRatio)
{
    cout << "\n---------- UniIdealTest setDampRatio ----------" << endl;
    double val = 2000;
    mat1_.setDampRatio(val);
    EXPECT_DOUBLE_EQ(val, mat1_.damp_ratio_);

    // input an negative value
    val = -100;
    mat1_.setDampRatio(val);
    EXPECT_DOUBLE_EQ(val, mat1_.damp_ratio_);
};


// Tests that the UniIdeal::setDampCoef() method does Abc.
TEST_F(UniIdealTest, setDampCoef)
{
    cout << "\n---------- UniIdealTest setDampCoef ----------" << endl;
    double val = 2000;
    mat1_.setDampCoef(val);
    EXPECT_DOUBLE_EQ(val, mat1_.damp_coef_);

    // input an negative value
    val = -100;
    mat1_.setDampCoef(val);
    EXPECT_DOUBLE_EQ(val, mat1_.damp_coef_);
};


// Tests that the UniIdeal::setMu() method does Abc.
TEST_F(UniIdealTest, setMu)
{
    cout << "\n---------- UniIdealTest setMu ----------" << endl;
    double val = 2000;
    mat1_.setMu(val);
    EXPECT_DOUBLE_EQ(val, mat1_.mu_);

    // input an negative value
    val = -100;
    mat1_.setMu(val);
    EXPECT_DOUBLE_EQ(val, mat1_.mu_);
};


// Tests that the UniIdeal::setLimitedTensileStrain() method does Abc.
TEST_F(UniIdealTest, setLimitedTensileStrain)
{
    cout << "\n---------- UniIdealTest setLimitedTensileStrain ----------" << endl;
    double val = 2000;
    mat2_.setLimitedTensileStrain(val);
    EXPECT_DOUBLE_EQ(val, mat2_.limited_tensile_strain_);

    // input an negative value
    val = -100;
    mat1_.setLimitedTensileStrain(val);
    EXPECT_DOUBLE_EQ(9.99e99, mat1_.limited_tensile_strain_);

};


// Tests that the UniIdeal::setLimitedCompressStrain() method does Abc.
TEST_F(UniIdealTest, setLimitedCompressStrain)
{
    cout << "\n---------- UniIdealTest setLimitedCompressStrain ----------" << endl;
    double val = 2000;
    mat1_.setLimitedCompressStrain(val);
    EXPECT_DOUBLE_EQ(-9.99e99, mat1_.limited_compress_strain_);

    // input an negative value
    val = -100;
    mat2_.setLimitedCompressStrain(val);
    EXPECT_DOUBLE_EQ(val, mat2_.limited_compress_strain_);
};


// Tests that the UniIdeal::setLimitedStrain() method does Abc.
TEST_F(UniIdealTest, setLimitedStrain)
{
    cout << "\n---------- UniIdealTest setLimitedStrain ----------" << endl;
    double val1 = 1000, val2 = -1000;
    mat1_.setLimitedStrain(val1, val1);
    EXPECT_DOUBLE_EQ(val1, mat1_.limited_tensile_strain_);
    EXPECT_DOUBLE_EQ(-9.99e99, mat1_.limited_compress_strain_);

    mat2_.setLimitedStrain(val1, val2);
    EXPECT_DOUBLE_EQ(val1, mat2_.limited_tensile_strain_);
    EXPECT_DOUBLE_EQ(val2, mat2_.limited_compress_strain_);

    mat3_.setLimitedStrain(val2, val1);
    EXPECT_DOUBLE_EQ(9.99e99, mat3_.limited_tensile_strain_);
    EXPECT_DOUBLE_EQ(-9.99e99, mat3_.limited_compress_strain_);

    mat4_.setLimitedStrain(val2, val2);
    EXPECT_DOUBLE_EQ(9.99e99, mat4_.limited_tensile_strain_);
    EXPECT_DOUBLE_EQ(val2, mat4_.limited_compress_strain_);
};


// Tests that the UniIdeal::isYield() method does Abc.
TEST_F(UniIdealTest, isYield)
{
    cout << "\n---------- UniIdealTest isYield ----------" << endl;
    // positive
    EXPECT_TRUE(mat2_.isYield(20));
    EXPECT_TRUE(mat2_.isYield(30));
    EXPECT_TRUE(mat2_.isYield(40));

    // negative
    EXPECT_TRUE(mat2_.isYield(-20));
    EXPECT_TRUE(mat2_.isYield(-30));
    EXPECT_TRUE(mat2_.isYield(-40));

    // yield_stress < stress < yield_stress
    EXPECT_FALSE(mat2_.isYield(-10));
    EXPECT_FALSE(mat2_.isYield(-0));
    EXPECT_FALSE(mat2_.isYield(10));
};


// Tests that the UniIdeal::Eq() method does Abc.
TEST_F(UniIdealTest, Eq)
{
// hard to evaluate, test_uniideal_eq.cpp has been writen to test it by
// comparing with theory results
};


// Tests that the UniIdeal::info() method does Abc.
TEST_F(UniIdealTest, info)
{
    // do not need to test
};
